#include "clientToService.h"
#include "json/json.h"          //new version

#include <iostream>
#include <sys/time.h>           //  for function ->  gettimeofday(&tval, NULL);

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <time.h>
#include <string>
#include <ctime>

//Socket
#include <arpa/inet.h>
#include <sys/shm.h>
#include<errno.h>


using namespace std;

int getTime_self();
void getLocalTime_self(char* buf, size_t size = 50);
string getLocalTime_self();
//

static const int MAXLINE = 4096; 

int string2Json(const std::string &info, Json::Value &root);
std::string hearBeatInit(const string robotName);
std::string helloBeatInit(const string robotName, const string taskDescribe="client");

static std::mutex mutexSendInfo;

struct sockInfo
{
    /* data */
    struct sockaddr_in addr;
    int fd;
};
struct sockInfo infos[512];

//ID: robot name,  eg. ID="Robot001"
ClientToService::ClientToService(const string ID, const string serviceIp, const int servicePort, const int autoReconnNum, 
                                    const int jsonAndimgBuffSize, const string taskDescribe, const int reconnectInterval
                                ):
                                    mi_reconnectInterval(reconnectInterval),
                                    m_selfName(ID), 
                                    m_serviceIp(serviceIp),
                                    m_servicePort(servicePort),    //39.102.41.21
                                    m_reconnectNum(autoReconnNum),

                                    //m_connectNumber(0),
                                    m_connfd(-1),
                                    m_exit(false),
                                    m_connStatus(false),
                                    mp_jsonAndImgBuff(NULL),
                                    m_jsonAndImgBuffSize(jsonAndimgBuffSize),
                                    m_taskDescribe(taskDescribe)
{
    sem_init(&m_sem, 0, 0);

    if(autoReconnNum < 0){
        mb_autoReconnect = true;
    }
    printf(">>> clientToService.cpp::  selfName is %s\n", m_selfName.c_str());

    mp_jsonAndImgBuff = new char[jsonAndimgBuffSize];

    //m_working()function is used to communicate with win_service and can be reconnected
    printf("\033[1;32m%s ClientToService class-> m_working() running \033[0m\n", m_selfName.c_str());
    m_keepConnectThread_t = thread(&ClientToService::m_keepConnectThread, this, mi_reconnectInterval);
    m_communiteThread_t = thread(&ClientToService::m_communiteThread, this);
}
ClientToService::~ClientToService()
{
    sem_destroy(&m_sem);

    if(m_communiteThread_t.joinable()){
        m_communiteThread_t.join();
    }
    printf("\033[1;32m%s ClientToService class-> m_working()->m_communiteThread_t exited \033[0m\n", m_selfName.c_str());
    if(m_keepConnectThread_t.joinable()){
        m_keepConnectThread_t.join();
    }
    printf("\033[1;32m%s ClientToService class-> m_working()->m_keepConnectThread_t exited \033[0m\n", m_selfName.c_str());

    if(mp_jsonAndImgBuff != NULL){
        delete[] mp_jsonAndImgBuff;
        mp_jsonAndImgBuff = NULL;
    }
    //printf("ClientToService.cpp::  \033[1;32mclass: ~ClientToService() run \033[0m\n");
}

//m_working()function is used to communicate with win_service and can be reconnected
//reconnectInterval
// int ClientToService::m_working(const int reconnectInterval)
// {
//     printf("\033[1;32m%s ClientToService class-> m_working() running \033[0m\n", m_selfName.c_str());
//     m_keepConnectThread_t = thread(&ClientToService::m_keepConnectThread, this, reconnectInterval);
//     m_communiteThread_t = thread(&ClientToService::m_communiteThread, this);

//     if(m_communiteThread_t.joinable()){
//         m_communiteThread_t.join();
//     }
//     printf("\033[1;32m%s ClientToService class-> m_working()->m_communiteThread_t exited \033[0m\n", m_selfName.c_str());

//     if(m_keepConnectThread_t.joinable()){
//         m_keepConnectThread_t.join();
//     }
//     printf("\033[1;32m%s ClientToService class-> m_working()->m_keepConnectThread_t exited \033[0m\n", m_selfName.c_str());
//     return 0;
// }



int ClientToService::m_keepConnectThread(const int reconnectInterval)
{
    //heartbeat HZ
    const int heartbeat_hz = 5;
    int count = 0;
    int reconnectNum = m_reconnectNum;
    while (!m_getExit())
    {
        count %= 1000;
        count++;
        //std::unique_lock<mutex> lck(m_mutexConnStatus);
        if(m_getConnStatus() ){
            int res = -1;
            string hearBeart_buf = "*****" + hearBeatInit(m_selfName) + "#####";
            res = m_sendStr2service(hearBeart_buf);
            printf("%s: send heartbeat to service %s\n",m_selfName.c_str(), (getLocalTime_self()).c_str());

            if(res == -1){
                perror("ClientToService.cpp::  client send fail \n");
            }
            this_thread::sleep_for(chrono::seconds(heartbeat_hz));
            continue;
        }
        // sleep(5);
        // continue;

        if(mb_autoReconnect){
            printf("\033[1;35mwarning:\033[0m %s:ClientToService.cpp reconnect: %d\n",m_selfName.c_str(), count);
            fflush(stdout);

            if (m_connect() == 0){
                m_setConnStatus(true);
                count = 0;
                printf("\033[1;32m client to service reconnect success\n\033[0m");
                continue;
            }
            else{
                printf("\033[1;35mwarning:\033[0m %s:ClientToService.cpp reconnect failed \n",m_selfName.c_str());
            }

            printf("current connect status: %d\n", m_getConnStatus());
            this_thread::sleep_for(chrono::seconds(reconnectInterval));
            continue;
        }

        if(reconnectNum>0){
            reconnectNum--;
            printf("\033[1;35mwarning:\033[0m %s: ClientToService.cpp reconnect: %d\n",m_selfName.c_str(), count);
            if (m_connect() == 0){
                m_setConnStatus(true);
                count = 0;
                printf("client to service connect success\n");
                continue;
            }
            this_thread::sleep_for(chrono::seconds(reconnectInterval));
            continue;
        }
              
    }//while
   
    printf(">>>>>> m_keepConnectThread exited\n");
    return 0;
}


int ClientToService::m_communiteThread()
{
    fd_set rfds;
    struct timeval tv;
    int retval, maxfd;
    while (!m_getExit()){
        this_thread::sleep_for(chrono::seconds(2));
        if(m_getConnStatus()){
            printf(">>> \033[1;32m %s connect success \033[0m\n", m_selfName.c_str()); 
            char buf_recv[1024];
            //communication
            Json::Value root;
            unsigned int show_heartbeat_num = 0;
            cout << endl;
            int res = 0;
            while(!m_getExit()){
                
                /*把可读文件描述符的集合清空*/
                FD_ZERO(&rfds);
                /*把标准输入的文件描述符加入到集合中*/
                FD_SET(0, &rfds);
                maxfd = 0;
                /*把当前连接的文件描述符加入到集合中*/
                FD_SET(m_connfd, &rfds);
                /*找出文件描述符集合中最大的文件描述符*/
                if(maxfd < m_connfd)
                    maxfd = m_connfd;
                /*设置超时时间*/
                tv.tv_sec = 10; // s
                tv.tv_usec = 0;

                /*等待聊天*/
                retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
                if(retval == -1){
                    printf("select出错，客户端程序退出\n");
                    break;
                }
                else if(retval == 0){
                    printf("\r客户端没有任何输入信息，并且服务器也没有信息到来，waiting...");
                    fflush(stdout);
                    break;
                }
                else{
                    /*服务器发来了消息*/
                    if(FD_ISSET(m_connfd, &rfds)){
                        memset(buf_recv, 0, sizeof(buf_recv));
                       
                        res = recv(m_connfd, buf_recv, sizeof(buf_recv),0);
                        
                        if(res > 0){
                            if( string2Json(buf_recv, root) == -1){
                                printf("recv buff has a problems that cannot be parsed correctly\n");
                                break;
                            }
                            //1.helloBeat response
                            if(root.isMember("Heartbeat") ){ 
                                // printf("\r%s  recv is heartbeat %u", m_selfName.c_str(), show_heartbeat_num++);
                                // fflush(stdout);
                                continue;
                            }
                            if(root.isMember("DetectResponse") && root["DetectResponse"].isMember("Command")){
                                //cout << "recv tempAlarm response" << endl;
                                continue;
                            }

                            //cout <<"recvThread::  "<< buf_recv << endl;
                            printf("recv unknown\n\n");

                        }
                        else if(res ==0){
                            printf(">>> \033[1;35m warnning:\033[0m service disconnected\n");
                            break;   
                        }
                        else{
                            perror("recv");
                            break;
                        } 
                    }// service send message
                }
                
            }//while
        }//if connect success
        else{
            continue;
        }
        close(m_connfd);
        m_connfd = -1;
        m_setConnStatus(false);
        
        if(m_getExit()){
            printf("m_communiteThread  exited\n");
            break;
        }

    }//while
    close(m_connfd);
    printf(">>>>>>m_communiteThread  exited\n");
    return 0;
}

int ClientToService::m_connect()
{
    //connect service with ip and port
    struct sockaddr_in  servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(m_serviceIp.c_str());
    //inet_pton(AF_INET, m_serviceIp.c_str(), &servaddr.sin_addr.s_addr);
    servaddr.sin_port = htons(m_servicePort);

    //ctreate socket for conmmunication
    m_connfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if( m_connfd == -1 ){      //AF_INET: IPV4   ;SOCK_STREAM: TCP, //若成功则返回一个sockfd (套接字描述符)
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return -1;
    }

    /* code */

    int res = connect(m_connfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    if(res == -1){
        printf("connect refused,ip:%s, port:%d \n", m_serviceIp.c_str(), m_servicePort);
        close(m_connfd);
        return -1;
    }
    else{
        //hello packet
        string helloBeart_buf = "*****" + helloBeatInit(m_selfName, m_taskDescribe) + "#####";
        send(m_connfd, helloBeart_buf.c_str(), strlen(helloBeart_buf.c_str() ) , 0);//向TCP连接的另一端发送数据。
        //printf("%s\n", helloBeart_buf.c_str());
        //printf("%d\n", (int)sizeof(helloBeart_buf));

        char recvBuf[MAXLINE];
        memset(recvBuf, 0 ,sizeof(recvBuf));
        int len = recv(m_connfd, recvBuf, sizeof(recvBuf), 0);

        if(len == 0){
            printf("ClientToService.cpp:: connect break off \n");
            close(m_connfd);
            return -1;
        }
        else if (len < 0){
            printf("ClientToService.cpp:: recv error\n");
            close(m_connfd);
            return -1;
        }

        Json::Value root;
        if(string2Json(recvBuf, root) == 0 ){
            if(root["HelloACK"].isNull()){
                std::cout << "HelloACK null\n"<<"recv: "<<recvBuf<<endl;
                close(m_connfd);
                return -1;
            }
            printf(">>>>>>linux_service hello connect success with win_service\n: %s\n", recvBuf);
            
            return 0;
        }
        close(m_connfd);
        return -1;
    }
   

#if 0
    //ctreate socket for conmmunication
    m_connfd = socket(AF_INET, SOCK_STREAM, 0);
    if( m_connfd == -1 ){      //AF_INET: IPV4   ;SOCK_STREAM: TCP, //若成功则返回一个sockfd (套接字描述符)
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        close(m_connfd);
        return -1;
    }
    //connect service with ip and port
    struct sockaddr_in  servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, m_serviceIp.c_str(), &servaddr.sin_addr.s_addr);
    servaddr.sin_port = htons(m_servicePort);


/* code */
    while(1){
        printf("\033[1;32m%s: connectting ip:%s, port:%d\n\033[0m",m_selfName.c_str(), m_serviceIp.c_str(), m_servicePort);
        int res = connect(m_connfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
        exit(0);
        if(res == -1){
            printf("connect refused \n");
            break;
        }

        //hello packet
        string helloBeart_buf = "*****" + helloBeatInit(m_selfName, m_taskDescribe) + "#####";
        send(m_connfd, helloBeart_buf.c_str(), strlen(helloBeart_buf.c_str() ) , 0);//向TCP连接的另一端发送数据。

        char recvBuf[MAXLINE];
        memset(recvBuf, 0 ,sizeof(recvBuf));

        int len = recv(m_connfd, recvBuf, sizeof(recvBuf), 0);
        if(len == 0){
            printf("ClientToService.cpp:: connect break off \n");
            break;
        }
        else if (len < 0){
            printf("ClientToService.cpp:: recv error\n");
            break;
        }

        Json::Value root;
        if(string2Json(recvBuf, root) == 0 ){
            if(root["HelloACK"].isNull()){
                std::cout << "HelloACK null\n";
                break;
            }
            printf("recv HelloACK\n");
            return 0;
        }
        break;
    }
    close(m_connfd);
    m_connfd = -1;
    return -1;
    
#endif

}

//return len: send success and -1: failed
int ClientToService::m_sendStr2service(const string &strInfo)
{
    //printf("debug:  ClientToService.cpp::  sendStr2win()\n");
    if(!m_getConnStatus()){
        printf("client->service unconnect, send info failed\n");
        return -1;
        
    }
    unique_lock<mutex> lock(mutexSendInfo);
    int len = send(m_connfd, strInfo.c_str(), strInfo.size(), 0);
    return len;
}
int ClientToService::m_sendBuff2Service(const char *buff, const int buffLen)
{
    //printf("debug:  ClientToService.cpp::  sendStr2win()\n");
    if(!m_getConnStatus()){
        printf("client--service unconnect\n");
        return -1;
        
    }
    
    unique_lock<mutex> lock(mutexSendInfo);
    int len = send(m_connfd, buff, buffLen, 0);
    //sleep(1);
    return len;
}


//default: false
bool ClientToService::m_getExit()
{
    unique_lock<mutex> lock(m_mutexExit);
    return m_exit;
}
void ClientToService::m_setExit(const bool val)
{
    unique_lock<mutex> lock(m_mutexExit);
    m_exit = val;
}

//defaul: false
bool ClientToService::m_getConnStatus()
{
    unique_lock<mutex> lock(m_mutexConnStatus);
    return m_connStatus;
}
void ClientToService::m_setConnStatus(const bool val)
{
    unique_lock<mutex> lock(m_mutexConnStatus);
    m_connStatus = val;
}

string ClientToService::m_createJson_alarm(const int imgBuffSize, const float fx, const float fy)
{
    std::string strjson;
    Json::Value root;
    Json::StreamWriterBuilder writeBuild;
    std::ostringstream os;


    root["ImgBuffSize"] = imgBuffSize;
    root["DetectCommand"]["Command"] = "UpdatePic";
    root["DetectCommand"]["Args"]["ImageProcessMode"] = "TempAlarm";
    root["DetectCommand"]["Args"]["Positions"] = 0;
    root["DetectCommand"]["Args"]["File_Path"] = "imgPath"; //"/home/ljl/catkin_tcp/socket_part/test/900005.png";     //linux service addr
    root["DetectCommand"]["Args"]["Time"] = getLocalTime_self();
    root["DetectCommand"]["Args"]["Task_ID"] = "";

    root["DetectCommand"]["Args"]["fx"] = to_string(fx);
    root["DetectCommand"]["Args"]["fy"] = to_string(fy); 

    std::unique_ptr<Json::StreamWriter> jsonWriter(writeBuild.newStreamWriter());
    jsonWriter->write(root,&os);
    strjson = "*****" + os.str() + "#####";
    return strjson;
}

std::string ClientToService::m_createJson_deviceInfo(const float max, const float min, const float ave, const float diff, const int pos, const string time
                                                    , const int imgSize)
{
    std::string jsonStr;
    Json::Value  TunnelTemperature , root;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;

    TunnelTemperature["ID"] = m_selfName;
    TunnelTemperature["Maximum"] = max;
    TunnelTemperature["Minimal"] = min;
    TunnelTemperature["Average"] = ave;
    TunnelTemperature["Diff"] = ave;
    TunnelTemperature["CurrentPosition"] = pos;
    TunnelTemperature["CurrentTime"] = time;

    root["TunnelTemperature"] = Json::Value(TunnelTemperature);

    if(imgSize>0){
        root["ImgBuffSize"] = imgSize;
    }

	std::unique_ptr<Json::StreamWriter> jsonWrite(writerBuilder.newStreamWriter() );
	if(jsonWrite->write(root, &os) != 0) return "null";
	jsonStr = os.str();

	
    return "*****" + jsonStr + "#####";
}


///////////////////////////////////////////////////

std::string hearBeatInit(const string robotName)
{
    std::string jsonStr;
    Json::Value root, Hear;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;
    
    char buf[50];
    memset(buf, 0, sizeof(buf));
    getLocalTime_self(buf, 50);

    root["ID"] = robotName;
    root["CurrentTime"] = buf;
    Hear["Heartbeat"] = Json::Value(root);

    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(Hear, &os);
    jsonStr = os.str();
    return jsonStr;
}

std::string helloBeatInit(const string robotName, const string taskDescribe)
{

    std::string jsonStr;
    Json::Value root, Hello;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;

    char buf[50];
    memset(buf, 0, sizeof(buf));
    getLocalTime_self(buf,50);

    root["ID"] = robotName;
    root["TaskIdx"] = -1;
    root["TaskDescribe"] = taskDescribe;
    //root["Time"] = buf;
    Hello["Hello"] = Json::Value(root);

    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(Hello, &os);
    jsonStr = os.str();

  //  jsonStr = root.toStyledString();
    return jsonStr;
}


int string2Json(const std::string &info, Json::Value &root)
{
    if (info.empty()) return -1;

    bool res;
    JSONCPP_STRING errs;
    //Json::Value root;
    Json::CharReaderBuilder readerBuilder;
    std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
    //    reader.parse(jsonStr,root);       //for old version

    //string startMarkStr = "*****";
    //string endMarkStr = "#####";
    const int len = info.size();
    if(len <10){
        printf("string2Json(): string size %d < 10\n", len);
        return -1;
    }
    if(info.substr(0,5) == "*****" && info.substr(len-5,5 ) == "#####"){
        const string &jsonStr = info.substr(5, len-10);
        res = jsonReader->parse(jsonStr.c_str(), jsonStr.c_str()+jsonStr.length(), &root, &errs);        //new version return bool

        if (!res || !errs.empty()) {
            std::cout << " mString2Json :: parseJson err. " << errs << std::endl;
            return -1;
        }
    }
    else{
        printf("string start and end not ***** #####\n");
        return -1;
    }
    return 0;
}


void getLocalTime_self(char* buf, size_t size){
    //char buffer[50];
    char* &buffer1 = buf;

    struct timeval tv; 
    struct tm now_time;

    gettimeofday(&tv, NULL);
    //printf("%ld\n", tv.tv_sec);

     localtime_r(&tv.tv_sec, &now_time);
    // printf("%d-%d-%d %d-%d-%d\n", now_time.tm_year+1900, now_time.tm_mon+1, now_time.tm_mday, now_time.tm_hour, now_time.tm_min, now_time.tm_sec);

    snprintf(buffer1, 60, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", now_time.tm_year+1900, now_time.tm_mon+1, now_time.tm_mday, now_time.tm_hour, now_time.tm_min, now_time.tm_sec);
    //printf("%s\n", buffer1);

    return;
}

//#include <ctime>
string getLocalTime_self()
{
    // 基于当前系统的当前日期/时间 
	time_t now = time(0);

	// 把 now 转换为 tm 结构
	tm *gmtm = localtime(&now);
	//cout << gmtm->tm_year+1900 << "年" << gmtm->tm_mon+1 << "月" << gmtm->tm_mday << "日" << gmtm->tm_hour << ":" << gmtm->tm_min << ":" << gmtm->tm_sec<<endl;

    char buffer[60];
    snprintf(buffer, sizeof(buffer), "%.4d-%.2d-%02d %.2d:%.2d:%.2d", gmtm->tm_year+1900, gmtm->tm_mon+1, gmtm->tm_mday, gmtm->tm_hour,gmtm->tm_min, gmtm->tm_sec);

    string str = buffer;
    return buffer;
}
